package org.osami.androidui;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set; 

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle; 
import android.webkit.WebView;

import org.osami.utils.Logger;

class OSGiIntentReceiver extends BroadcastReceiver {
	OsgiRelay relay;
	
	Logger log = new Logger(this.getClass().getName());
	
	public OSGiIntentReceiver(OsgiRelay aRelay) {
		super( );
		relay = aRelay;
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		
		JSONObject json;
		
		Bundle bundle = intent.getExtras();
		String topic = bundle.getString("topic");
		String jsonString = bundle.getString("json");
		
		try {
			// TODO: Is JSON conversion really needed? Used for validation here.
			json = new JSONObject(jsonString);
			relay.osgiEventToJavascript(topic, json);
		} catch (JSONException e) {
			e.printStackTrace();
			log.e(e.getMessage());
		}		
	}
}

/** Basic class for listening to any Android Intent and converting it to JSON for javascript */
class IntentReceiver extends BroadcastReceiver {
	OsgiRelay relay;
	
	public IntentReceiver(OsgiRelay aRelay) {
		super( );
		relay = aRelay;
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		
		JSONObject json = new JSONObject();
		JSONObject jsonExtras = new JSONObject();
		
		Bundle bundle = intent.getExtras();
		
		try {
			
			json.put("intent", intent.getAction());
			json.put("type", intent.getType());
		
			Set<String> keys = bundle.keySet();
			for(String key : keys){
				Object value = bundle.get(key);
				
				try{
					jsonExtras.put(key, new JSONArray(value.toString()));
					continue;
				}
				catch(JSONException e){
					// Intentional do-nothing
				}
				
				try{
					jsonExtras.put(key, new JSONObject(value.toString()));
				}
				catch(JSONException e){
					jsonExtras.put(key, value);
				}				
			}
			
			json.put("extras", jsonExtras);
			
			relay.intentToJavascript(json);
			
		} catch (JSONException e) {
			e.printStackTrace();
			//log.e(e.getMessage());
		}		
	}
}

public class OsgiRelay {
	WebView webview;

	Logger log = new Logger(this.getClass().getName());
	
	/// Topic->intent receiver
	Hashtable<String, BroadcastReceiver> receivers = new Hashtable();
		  
	public OsgiRelay(WebView webview){
		this.webview = webview;
	}
	
	/** Utility for adding intent receiver to list of registered receivers.
	 * If receiver exists with same aIntentAction, the old is unregistered and removed from list.
	 * 
	 * @param aIntentAction
	 * @param aBroadcastReceiver
	 */
	protected void addIntentReceiver(String aIntentAction, BroadcastReceiver aBroadcastReceiver){
		
		Context context = webview.getContext();
		
		if(receivers.containsKey(aIntentAction))
		{
			log.df("Unregistering listener for previous Intent %s", aIntentAction);
			BroadcastReceiver old = receivers.remove(aIntentAction);
			context.unregisterReceiver(old);
		}
		
		receivers.put(aIntentAction, aBroadcastReceiver);
	}
	
	public void setupIntentListener(String aIntentAction){
		Context context = webview.getContext();
		
		IntentReceiver receiver = new IntentReceiver(this);
		IntentFilter osgiFilter = new IntentFilter(aIntentAction);
		context.registerReceiver(receiver, osgiFilter);
		
		addIntentReceiver(aIntentAction, receiver);
	}
	
	/** Javascript API for listening OSGi events.
	 * 
	 * This will pass the request to org.osami.appevent bundle via Intent(org.osami.OSGI_2_INTENT).
	 *  The Intent expects 'topic' and 'intent' parameters
	 *   topic - The OSGi event type to listen for
	 *   intent - The Android Intent type to broadcast on receive
	 *   
	 * The bundle will then register itself as receiver for the given OSGi topic
	 * and pass the received OSGi events forward as Intents containing
	 * extra parameters: "topic" and "json". "json" holds the OSGi event properties as JSON string.
	 * 
	 * This method will automatically add listener for the Intent specified via androidIntent parameter. 
	 * The OSGi events are passed to javascript application.
	 * 
	 * Example of use from javascript:
	 * 
	 * OSAMI.setupOSGi2IntentConversion("org.osami.PROGRAMS", "org.osami.intent.PROGRAMS");
	 * OSAMI.addOsgiEventListener("org.osami.PROGRAMS", function(topic, data){
	 * 	   // Do something with data
	 * });;
	 * 
	 * Depends on osami_fin.js javascript library(webview must load).
	 * 
	 * */
	public void setupOSGi2IntentConversion(String aOSGiTopic, String aIntentAction)
	{
		
		Context context = webview.getContext();
		
		Intent i = new Intent();		
		i.setAction("org.osami.OSGI_2_INTENT");
		i.putExtra("topic", aOSGiTopic);
		i.putExtra("intent", aIntentAction);
		
		context.sendBroadcast(i);
		
		// Setup listener for sending the events to javascript application
		OSGiIntentReceiver osgi2IntentReceiver = new OSGiIntentReceiver(this);
		IntentFilter osgiFilter = new IntentFilter(aIntentAction);
		context.registerReceiver(osgi2IntentReceiver, osgiFilter);
		
		addIntentReceiver(aIntentAction, osgi2IntentReceiver);
	}
	
	/** Post OSGi event */
	public void postOSGiEvent(String aOSGiTopic, String aJSONString)
	{
		
		// Validate JSON
		JSONObject json;
		try {
			json = new JSONObject(aJSONString);
		} catch (JSONException e) {
			e.printStackTrace();
			return;
		}
		
		Context context = webview.getContext();
		
		Intent i = new Intent();		
		i.setAction("org.osami.OSGI_EVENT");
		i.putExtra("topic", aOSGiTopic);
		i.putExtra("json", json.toString());
		
		context.sendBroadcast(i);		
	}
	
	/**
	 * Supported JSON fields
	 * {
	 * 	"action" : "my.android.INTENT",
	 *  "type"   : "my.intent.TYPE",
	 *  "extras" : {
	 *  	"key1" : "value1"
	 *   }
	 * }
	 * @param aJSONString
	 */
	public void postAndroidIntent(String aJSONString)
	{
		Intent i = new Intent();
		 
		JSONObject json;
		try {
			json = new JSONObject(aJSONString);
			
			String action = json.getString("action");
			if(action == null){
				log.d("Missing action!");
				return;
			}			
			i.setAction(action);

			if (json.has("type")) {
				i.setType(json.getString("type"));
			}

			JSONObject extras = json.getJSONObject("extras");
			Iterator<String> keys = extras.keys();

			while (keys.hasNext()) {
				String key = keys.next();
				i.putExtra(key, extras.getString(key));
			}

		} catch (JSONException e) {
			log.e(e.toString());
			return;
		}
		
		Context context = webview.getContext();
		context.sendBroadcast(i);		
		
	}
	
	/** Pass Android intent as JSON to javascript event handlers */
	public void intentToJavascript(JSONObject aJSONObject)
	{
		String jsonString = aJSONObject.toString();
		String code = String.format( "javascript:window.OSAMI.%s(%s);", "_processAndroidIntent", jsonString);
        webview.loadUrl(code);
	}
	
	/** Pass a captured OSGi event to javascript application for processing( called from OSGiIntentReceiver )
	 * */ 
	public void osgiEventToJavascript(String aOSGiTopic, JSONObject aJSONObject){
		String jsonString = aJSONObject.toString();
		String code = String.format( "javascript:window.OSAMI.%s('%s', %s);", "_processOSGiEvent", aOSGiTopic, jsonString);
        webview.loadUrl(code);
	}
}
